﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Numerics;
using System.Text;
using System.Threading;
using UtilZ.Dotnet.Ex.Base;

namespace ConsoleApp1
{

    public class LinuxUsage
    {

        /// <summary>
        /// 获取进程CPU及内存使用情况
        /// </summary>
        /// <param name="processType">进程名称</param>
        internal void GetCPUandMemory(string processName)
        {
            try
            {
                var str = ExecuteCommand("ps -aux");
                var str_l = str.Where(o => !string.IsNullOrWhiteSpace(o)).Select(o => o.Split(' ', StringSplitOptions.RemoveEmptyEntries));
                var c = str_l.FirstOrDefault();
                var l = str_l.Skip(1);

                var data = l.Select(o =>
                {
                    Dictionary<string, string> temp = new Dictionary<string, string>();
                    for (var i = 0; i < c.Length; i++)
                    {
                        if (i == 10)
                        {
                            temp.Add(c[i], string.Join(" ", o.Skip(10)));
                        }
                        else
                        {
                            temp.Add(c[i], o[i]);
                        }
                    }
                    return temp;
                });

                var ps = data.FirstOrDefault(o => o["COMMAND"].Contains(processName));
                if (ps != null)
                {
                    var cpuPercent = Convert.ToDouble(ps["%CPU"]);//CPU 占用比例
                    var memoryValue = Convert.ToInt64(ps["RSS"]);//真实内存
                    var memoryPercent = Convert.ToDouble(ps["%MEM"]);//内存占用比例
                    var memoryOther = Convert.ToInt64(ps["VSZ"]);//虚拟内存

                    Console.WriteLine($"CPU:{cpuPercent}%   Mem:{memoryValue}Byte");
                }
                else
                {
                    Console.WriteLine("获取进程CPU使用率失败");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"获取进程CPU使用率异常,{ex.Message}");
            }
        }






        /// <summary>
        /// 获取某进程或多个进程的cpu使用率
        /// processType 说明：dotnet、test1.dll|test2.dll
        /// </summary>
        /// <param name="processName"></param>
        internal void GetCPUByProcStat(IEnumerable<string> processNames)// string processName = "dotnet")
        {
            try
            {
                //获取到相关进程的PID
                var str = ExecuteCommand("ps -aux");
                var str_l = str.Where(o => !string.IsNullOrWhiteSpace(o)).Select(o => o.Split(' ', StringSplitOptions.RemoveEmptyEntries));
                var c = str_l.FirstOrDefault();
                var l = str_l.Skip(1);

                var data = l.Select(o =>
                {
                    Dictionary<string, string> temp = new Dictionary<string, string>();
                    for (var i = 0; i < c.Length; i++)
                        if (i == 10) temp.Add(c[i], string.Join(" ", o.Skip(10)));
                        else temp.Add(c[i], o[i]);
                    return temp;
                });

                List<int> pids = new List<int>();

                int processNameCount = processNames.Count();
                for (int i = 0; i < processNameCount; i++)
                {
                    var ps = data.Where(o => o["COMMAND"].Contains(processNames.ElementAt(i))).ToList();
                    for (int j = 0; j < ps.Count(); j++)
                    {
                        pids.Add(Convert.ToInt32(ps[j]["PID"]));//PID
                    }
                }

                //第一次获取CPU总的运行时间
                int totalCPUTimeT1 = GetTotalCPUTime();
                //第一次获取进程的运行时间
                int processCPUTimeT1 = GetProcessCPUTime(pids);

                Thread.Sleep(1000);

                //第二次获取CPU总的运行时间
                int totalCPUTimeT2 = GetTotalCPUTime();
                //第二次获取进程的运行时间
                int processCPUTimeT2 = GetProcessCPUTime(pids);

                //进程的CPU使用率
                Double processCPUUse = ((Convert.ToDouble(processCPUTimeT2 - processCPUTimeT1) / Convert.ToDouble(totalCPUTimeT2 - totalCPUTimeT1)) * 100);
                Console.WriteLine($"CPU:{processCPUUse.ToString("F")}");
                //Double processCPUUse = ((Convert.ToDouble(processCPUTimeT2 - processCPUTimeT1) / Convert.ToDouble(totalCPUTimeT2 - totalCPUTimeT1)) * 100).ToString("F");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"获取CPU使用率异常,{ex.Message}");
            }
        }
        /// <summary>
        /// 通过/proc/stat文件查看所有的CPU活动信息，返回CPU活动时间
        /// </summary>
        /// <returns></returns>
        private static int GetTotalCPUTime()
        {
            int totalCPUTime = 0;
            var cpus = ExecuteCommand("cat /proc/stat");
            var str_cpu = cpus.Where(o => !string.IsNullOrWhiteSpace(o)).Select(o => o.Split(' ', StringSplitOptions.RemoveEmptyEntries));
            var cpu = str_cpu.FirstOrDefault();
            //totalCPUTime = user + nice + system + idle + iowait + irq + softirq
            totalCPUTime = Convert.ToInt32(cpu[1])
                + Convert.ToInt32(cpu[2])
                + Convert.ToInt32(cpu[3])
                + Convert.ToInt32(cpu[4])
                + Convert.ToInt32(cpu[5])
                + Convert.ToInt32(cpu[6])
                + Convert.ToInt32(cpu[7]);
            return totalCPUTime;
        }

        /// <summary>
        /// 通过/proc/[PID]/stat 文件查看某一进程的CPU活动信息，返回进程CPU活动时间
        /// </summary>
        /// <returns></returns>
        private static int GetProcessCPUTime(List<int> pids)
        {
            int totalProcessCPUTime = 0;
            for (int i = 0; i < pids.Count; i++)
            {
                var cpus = ExecuteCommand($"cat /proc/{pids[i]}/stat");
                var str_cpu = cpus.Where(o => !string.IsNullOrWhiteSpace(o)).Select(o => o.Split(' ', StringSplitOptions.RemoveEmptyEntries));
                var cpu = str_cpu.FirstOrDefault();
                //processCPUTime = utime + stime
                totalProcessCPUTime += Convert.ToInt32(cpu[13]) + Convert.ToInt32(cpu[14]);
            }
            return totalProcessCPUTime;
        }






        /// <summary>
        /// 监控容器网卡的实时速率
        /// </summary>
        internal void GetDockerReceiveAndTransmit()
        {
            try
            {
                //第一次获取接口发送或接收的数据的
                var str1 = ExecuteCommand("cat /proc/net/dev");
                Thread.Sleep(1000);
                //第二次获取接口发送或接收的数据的
                var str2 = ExecuteCommand("cat /proc/net/dev");

                //第一次接收到的数据
                var firstData = GetFormatData(str1);
                var firstReceive = 0.0;//第一次收包
                var firstTransmit = 0.0;//第一次发包
                //var firstPs = firstData.FirstOrDefault(o => o["face"].Contains(name));
                if (firstData != null)
                {
                    foreach (var firstPs in firstData)
                    {
                        //第一次收包
                        firstReceive += Convert.ToDouble(firstPs["|bytes"]);
                        //第一次发包
                        firstTransmit += Convert.ToDouble(firstPs["multicast|bytes"]);
                    }
                }
                //第二次接收到的数据
                var secondData = GetFormatData(str2);
                var secondReceive = 0.0;//第二次收包
                var secondTransmit = 0.0;//第二次发包
                //var secondPs = secondData.FirstOrDefault(o => o["face"].Contains(name));
                if (secondData != null)
                {
                    foreach (var secondPs in secondData)
                    {
                        //第二次收包
                        secondReceive += Convert.ToDouble(secondPs["|bytes"]);
                        //第二次发包
                        secondTransmit += Convert.ToDouble(secondPs["multicast|bytes"]);
                    }
                }

                //接收速率
                //var receive = (secondReceive - firstReceive) / 1024;
                var receive = (secondReceive - firstReceive);
                //读取速率
                //var transmit = (secondTransmit - firstTransmit) / 1024;
                var transmit = (secondTransmit - firstTransmit);

                Console.WriteLine($"网卡速率,接收速率:{receive}  发送速率:{transmit}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"获取网卡速率异常,{ex.Message}");
            }
        }

        private IEnumerable<Dictionary<string, string>> GetFormatData(string[] str1)
        {
            var resArry = new string[] { "face", "|bytes", "multicast|bytes" };
            var str_1 = str1.Where(o => !string.IsNullOrWhiteSpace(o)).Select(o => o.Split(' ', StringSplitOptions.RemoveEmptyEntries));
            var str_2 = str_1.Skip(1);
            var c = str_2.FirstOrDefault();
            var l = str_2.Skip(1);

            var data = l.Select(o =>
            {
                Dictionary<string, string> temp = new Dictionary<string, string>();
                for (var i = 0; i < c.Length; i++)
                {
                    if (resArry.Contains(c[i]))
                    {
                        temp.Add(c[i], o[i]);
                    }
                }
                return temp;
            });
            return data;
        }









        /// <summary>
        /// 磁盘的读写实时速率
        /// </summary>
        internal void GetDiskReadAndWrite()
        {
            try
            {
                var str = ExecuteCommand("iostat -dx");
                var str_l = str.Where(o => !string.IsNullOrWhiteSpace(o)).Select(o => o.Split(' ', StringSplitOptions.RemoveEmptyEntries));
                var res = str_l.Skip(1);
                var c = res.FirstOrDefault();
                var l = res.Skip(1);

                var data = l.Select(o =>
                {
                    Dictionary<string, string> temp = new Dictionary<string, string>();
                    for (var i = 0; i < c.Length; i++)
                        temp.Add(c[i], o[i]);
                    return temp;
                });
                var read = 0.0;//磁盘读取速度
                var write = 0.0;//磁盘写入速度
                //var ps = data.FirstOrDefault();
                if (data != null)
                {
                    foreach (var ps in data)
                    {
                        read += Convert.ToDouble(ps["r/s"]);//磁盘读取速度
                        write += Convert.ToDouble(ps["w/s"]);//磁盘写入速度
                    }
                }

                Console.WriteLine($"磁盘,读速度:{read} 写速度:{write}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"获取磁盘读写速度异常,{ex.Message}");
            }
        }



        /// <summary>
        /// 执行 linux命令
        /// </summary>
        /// <param name="pmCommand"></param>
        /// <returns></returns>
        private static string[] ExecuteCommand(string pmCommand)
        {
            string text = ProcessEx.ExecuteCommand(pmCommand);
              return text.Split('\n');
        }

    }
}




/************************************************************************************************
[root@localhost ~]# netstat -tunp
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      1 192.168.0.105:51210     34.117.65.55:443        SYN_SENT    6046/firefox        
tcp        0      0 127.0.0.1:33551         127.0.0.1:54798         ESTABLISHED 32988/wpsoffice     
tcp        0      0 127.0.0.1:53860         127.0.0.1:35571         ESTABLISHED 32988/wpsoffice     
tcp        0      0 127.0.0.1:35571         127.0.0.1:53860         ESTABLISHED 32988/wpsoffice     
tcp        0      0 127.0.0.1:37025         127.0.0.1:42960         ESTABLISHED 35561/wps           
tcp        0      0 127.0.0.1:54798         127.0.0.1:33551         ESTABLISHED 32988/wpsoffice     
tcp        0      0 127.0.0.1:42960         127.0.0.1:37025         ESTABLISHED 35561/wps           
tcp        0      0 192.168.0.105:51212     34.117.65.55:443        ESTABLISHED 6046/firefox        
tcp        0      0 192.168.0.105:52950     34.120.208.123:443      ESTABLISHED 6046/firefox        
tcp        0      0 192.168.0.105:51206     34.117.65.55:443        ESTABLISHED 6046/firefox 


[root@localhost ~]# netstat -tunp
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:33551         127.0.0.1:54798         ESTABLISHED 32988/wpsoffice     
tcp        0      0 127.0.0.1:53860         127.0.0.1:35571         ESTABLISHED 32988/wpsoffice     
tcp        0      0 127.0.0.1:35571         127.0.0.1:53860         ESTABLISHED 32988/wpsoffice     
tcp        0     32 192.168.0.105:55468     20.42.65.85:443         LAST_ACK    -                   
tcp        0      0 192.168.0.105:52048     34.117.65.55:443        ESTABLISHED 6046/firefox        
tcp        0      0 127.0.0.1:54798         127.0.0.1:33551         ESTABLISHED 32988/wpsoffice     
tcp        0      0 192.168.0.105:60256     52.236.186.218:443      ESTABLISHED 51170/code          
tcp        0     32 192.168.0.105:55472     20.42.65.85:443         LAST_ACK    -                   
[root@localhost ~]# 


状态说明

    CLOSED	没有使用这个套接字[netstat 无法显示closed状态]
    LISTEN	套接字正在监听连接[调用listen后]
    SYN_SENT	套接字正在试图主动建立连接[发送SYN后还没有收到ACK]
    SYN_RECEIVED	正在处于连接的初始同步状态[收到对方的SYN，但还没收到自己发过去的SYN的ACK]
    ESTABLISHED	连接已建立
    CLOSE_WAIT	远程套接字已经关闭：正在等待关闭这个套接字[被动关闭的一方收到FIN]
    FIN_WAIT_1	套接字已关闭，正在关闭连接[发送FIN，没有收到ACK也没有收到FIN]
    CLOSING	套接字已关闭，远程套接字正在关闭，暂时挂起关闭确认[在FIN_WAIT_1状态下收到被动方的FIN]
    LAST_ACK	远程套接字已关闭，正在等待本地套接字的关闭确认[被动方在CLOSE_WAIT状态下发送FIN]
    FIN_WAIT_2	套接字已关闭，正在等待远程套接字关闭[在FIN_WAIT_1状态下收到发过去FIN对应的ACK]
    TIME_WAIT	这个套接字已经关闭，正在等待远程套接字的关闭传送[FIN、ACK、FIN、ACK都完毕，这是主动方的最后一个状态，在过了2MSL时间后变为CLOSED状态]
————————————————
版权声明：本文为CSDN博主「zzhongcy」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
原文链接：https://blog.csdn.net/zzhongcy/article/details/38851271
*************************************************************************************************/


/************************************************************************************************
[root@localhost ~]# ps -aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.1 177404 11528 ?        Ss   15:24   0:02 /usr/lib/systemd/systemd --switched-root --system --deserialize 18
root           2  0.0  0.0      0     0 ?        S    15:24   0:00 [kthreadd]
root           3  0.0  0.0      0     0 ?        I<   15:24   0:00 [rcu_gp]

USER        PID     %CPU 	%MEM    VSZ(虚拟内存) RSS（物理内存） TTY    	STAT    START   TIME 	COMMAND
root     	36828   0.2 	1.8 	273550596 	129264 	       pts/4	 Sl+ 	 16:25 	0:00 	./TestConsoleApp


[root@localhost ~]# ps -acux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.1 177404 11348 ?        Ss   15:24   0:03 systemd
root           2  0.0  0.0      0     0 ?        S    15:24   0:00 kthreadd
root           3  0.0  0.0      0     0 ?        I<   15:24   0:00 rcu_gp
root           4  0.0  0.0      0     0 ?        I<   15:24   0:00 rcu_par_gp
root           6  0.0  0.0      0     0 ?        I<   15:24   0:00 kworker/0:0H-kblockd
root           8  0.0  0.0      0     0 ?        I<   15:24   0:00 mm_percpu_wq
root           9  0.0  0.0      0     0 ?        S    15:24   0:00 ksoftirqd/0

1、ps -aux  是以BSD方式显示
ps   (Process Status)  进程状态
a (show processes for all users) 显示所有用户的进程
u (display the process's user/owner) 显示用户
x (also show processes not attached to a terminal)显示无控制终端的进程

2、ps -ef 是以System V方式显示，该种方式比BSD方式显示的多
e (all processes)显示所有用户的进程此参数的效果和指定"a"参数相同
f (ASCII art forest)用ASCII字符显示树状结构，表达程序间的相互关系

3、grep 
用于查找文件里符合条件的字符串。
grep指令用于查找内容包含指定的范本样式的文件，如果发现某文件的内容符合所指定的范本样式，预设grep指令会把含有范本样式的那一列显示出来。若不指定任何文件名称，或是所给予的文件名为"-"，则grep指令会从标准输入设备读取数据

    UID：启动这些进程的用户
     PID：进程的进程ID
    PPID：父进程的进程号（如果该进程是由另一个进程启动的）
    C：进程生命周期中的CPU利用率
    STIME：进程启动时的系统时间
    TTY：进程启动时的终端设备
    TIME：运行进程需要的累计CPU时间
    CMD：启动的程序名称
USER ：启动这些进程的用户
 PID：进程的进程ID
%CPU ：进程占用的CPU百分比 
%MEM ：占用内存的百分比 
VSZ ：该进程使用的虚拟內存量（KB） 
RSS ：该进程占用的固定內存量（KB）（驻留中页的数量） 
TTY ：进程启动时的终端设备
STAT ：进程的状态 
START ：该进程被触发启动时间 
TIME ：运行进程需要的累计CPU时间
COMMAND:启动的程序名称


Linux上进程有5种状态:
进程状态	含义	对应ps命令的状态码
运行	正在运行或在运行队列中等待	R 运行 runnable (on run queue) 
中断	休眠中, 受阻, 在等待某个条件的形成或接受到信号	S 中断 sleeping 
不可中断	收到信号不唤醒和不可运行, 进程必须等待直到有中断发生	D 不可中断 uninterruptible sleep (usually IO) 
僵死	进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放	Z 僵死 a defunct (”zombie”) process 
停止	进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行	T 停止 traced or stopped 

————————————————
版权声明：本文为CSDN博主「2014Team」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
原文链接：https://blog.csdn.net/u012965203/article/details/92830417
*************************************************************************************************/

/************************************************************************************************
[root@localhost ~]# iostat -c l
Linux 4.19.90-24.4.v2101.ky10.x86_64 (localhost.localdomain) 	2023年04月16日 	_x86_64_	(8 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           0.90    0.02    0.80    0.02    0.00   98.27

Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd


[root@localhost ~]# 


[root@localhost ~]# iostat -dx
Linux 4.19.90-24.4.v2101.ky10.x86_64 (localhost.localdomain) 	2023年04月16日 	_x86_64_	(8 CPU)

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz     d/s     dkB/s   drqm/s  %drqm d_await dareq-sz  aqu-sz  %util
dm-0             2.28    252.15     0.00   0.00    1.26   110.63    4.08    203.44     0.00   0.00    0.54    49.87    0.00      0.00     0.00   0.00    0.00     0.00    0.01   0.27
dm-1             0.01      0.22     0.00   0.00    0.17    19.11    0.05      0.20     0.00   0.00    0.24     4.00    0.00      0.00     0.00   0.00    0.00     0.00    0.00   0.00
dm-2             0.00      0.10     0.00   0.00    0.17    21.75    0.00      0.00     0.00   0.00    0.00     0.00    0.00      0.00     0.00   0.00    0.00     0.00    0.00   0.00
sda              2.49    257.95     0.04   1.51    1.11   103.43    3.39    203.84     0.74  18.00    0.63    60.20    0.00      0.00     0.00   0.00    0.00     0.00    0.00   0.27


[root@localhost ~]# 




    rrqm/s：  每秒进行 merge 的读操作数目.即 delta(rmerge)/s

    wrqm/s： 每秒进行 merge 的写操作数目.即 delta(wmerge)/s

    r/s: 每秒完成的读次数

    w/s: 每秒完成的写次数

    rkB/s: 每秒读数据量(kB为单位)

    wkB/s: 每秒写数据量(kB为单位)

    avgrq-sz:平均每次IO操作的数据量(扇区数为单位)

    avgqu-sz: 平均等待处理的IO请求队列长度

    await: 平均每次IO请求等待时间(包括等待时间和处理时间，毫秒为单位)

    svctm: 平均每次IO请求的处理时间(毫秒为单位)

    %util: 采用周期内用于IO操作的时间比率，即IO队列非空的时间比率，即一秒中有百分之多少的时间用于 I/O

    如果%util接近100%，说明产生的I/O请求太多，I/O系统已经满负荷

       idle小于70% IO压力就较大了，一般读取速度有较多的wait。

*************************************************************************************************/


/************************************************************************************************
[root@localhost ~]# df -h
文件系统               容量  已用  可用 已用% 挂载点
devtmpfs               3.3G     0  3.3G    0% /dev
tmpfs                  3.3G     0  3.3G    0% /dev/shm
tmpfs                  3.3G  9.7M  3.3G    1% /run
tmpfs                  3.3G     0  3.3G    0% /sys/fs/cgroup
/dev/mapper/klas-root   62G   20G   43G   32% /
tmpfs                  3.3G   20M  3.3G    1% /tmp
/dev/sda1             1014M  211M  804M   21% /boot
vmhgfs-fuse            477G  225G  253G   48% /mnt/hgfs
tmpfs                  667M   64K  667M    1% /run/user/0
[root@localhost ~]# 

*************************************************************************************************/

